package com.vinhvt.effiport.financial.ep.mapper;

import com.vinhvt.effiport.algorithm.genetic.GAsParameters;
import com.vinhvt.effiport.algorithm.genetic.bio.Individual;
import com.vinhvt.effiport.algorithm.genetic.bio.WeightGene;
import com.vinhvt.effiport.algorithm.genetic.mapper.IMapper;
import com.vinhvt.effiport.financial.ep.Asset;
import com.vinhvt.effiport.financial.ep.Portfolio;
import com.vinhvt.effiport.financial.ep.Underlying;
import com.vinhvt.effiport.util.Helper;


public class EffiPortMapper implements IMapper {
	private static Portfolio portfolio = null;	
	private static Individual individual = null;
	private static EffiPortGeneMap map = null;	
	
	public EffiPortMapper() {
		
	}
				
	public void setPortfolio(Portfolio p) {
		portfolio = p;
	}
	
	public static Individual getIndividual() {
		return individual;
	}
	
	public void setIndividual(Individual i) {
		individual = i;
	}	
	
	public static Portfolio getPortfolio() {
		return portfolio;
	}
	
	public static EffiPortMapper mapping(EffiPortGeneMap em) {
		map = em;
		
		if (individual == null) {
			//cipher
			double risk = GAsParameters.RISK_AVERSION;
			double variance = portfolio.getVariance();
			double profit = portfolio.getProfit();
			double fitness = ((1 - risk) * profit) - (risk * 10000 * variance); 
			fitness = fitness > 0 ? fitness : 0;			
			individual.setFitness(fitness);
			
			//cipherADN();
		}
		else if (portfolio == null) {
			//decipher
			
			portfolio = new Portfolio();
			//decipherADN();
			
			double variance = portfolio.getVariance();
			double risk = GAsParameters.RISK_AVERSION;
			double profit = portfolio.getProfit();		
			double fitness = ((1 - risk) * profit) - (risk * 10000 * variance);  		
			fitness = fitness > 0 ? fitness : 0;
			
			double w = portfolio.getTotalWeight(); 
			if ( w > 1.0 || w < 0.5) {
				fitness = Math.max(0, fitness/2.0);
			}
			
			individual.setFitness(fitness);
			
		}
		
		return null;
	}
	
	private Asset getAsset(int position) {
		String ticker = map.getName(position);
		EffiPortMapItem mapItem = (EffiPortMapItem) map.getMapItem(position);
		Underlying underlying = new Underlying(ticker, mapItem.getRateOfReturnList());
		Asset asset = new Asset(underlying);			
		
		return asset;
	}
	
	/**
	 * Builds the chromosome
	 */
	@Override
	public void cipherADN() {						
		int times = portfolio.getNumberOfAsset();
		for(int i = 0; i < times; i++) {			
			double weight = portfolio.getAssetAt(i).getWeight();
			WeightGene gene = new WeightGene(weight);
			individual.getChrWeight().addGene(gene);		
		}
	}
		
	@Override
	public void decipherADN() {
		int times = GAsParameters.INDIVIDUAL_SIZE;
		for(int i = 0; i < times; i++) {						
			double w = Helper.round(individual.getChrWeight().getGene(i).getValue().doubleValue());			
			Asset asset = this.getAsset(i);
			this.portfolio.addAsset(asset,w);
		}
	}
	

	public EffiPortGeneMap getMap() {
		return map;
	}
	
	

	@Override
	public void map2Coding(Object obj1) {
		this.portfolio = (Portfolio) obj1;
		individual = new Individual();		
		
		double risk = GAsParameters.RISK_AVERSION;
		double variance = portfolio.getVariance();
		double profit = portfolio.getProfit();
		//double fitness = ((1 - risk) * profit) - (risk * variance); 
		//fitness = fitness > 0 ? fitness : 0;
		double fitness = Math.max(0, 900 - variance);
		//Punishment
//		double weight = portfolio.calcTotalWeight();
//		if (weight > 1.0 || profit < 15) {
//			fitness -= GAsParameters.PUNISHMENT;
//		}
//		else if (weight < 0.5) {
//			fitness -= (GAsParameters.PUNISHMENT / 2.0);
//		}
		individual.setFitness(fitness);
		
		cipherADN();
		
	}

	@Override
	public void map2Solution(Object obj2) {
		this.individual = (Individual) obj2;
		portfolio = new Portfolio();
		decipherADN();
		
		double variance = this.portfolio.getVariance();
		double risk = GAsParameters.RISK_AVERSION;
		double profit = portfolio.getProfit();		
		double fitness = ((1 - risk) * profit) - (risk * 10000 * variance);  		
		fitness = fitness > 0 ? fitness : 0;
//		double fitness = Math.max(0, 225 - variance);
//		
//		//Punishment
//		double weight = portfolio.calcTotalWeight();
//		if (weight > 1.0 && profit < 15) {
//			fitness -= GAsParameters.PUNISHMENT;
//		}
//		else if (weight < 0.5) {
//			fitness -= (GAsParameters.PUNISHMENT / 2.0);
//		}
		double w = portfolio.getTotalWeight(); 
		if ( w > 1.0 || w < 0.5) {
			fitness = Math.max(0, fitness/2.0);
		}
		
		this.individual.setFitness(fitness);
		
	}
	
}
